#include <LitStdAfx.h>
#include "LitGameStateManager.h"
#include "LitCore.h"
#include "LitExceptions.h"
#include "LitCore.h"

namespace Lit
{
	GameStateManager::GameStateManager( ) : CoreComponent(COM_GAMESTATEMANAGER)
	{}
	GameStateManager::~GameStateManager( )
	{ clear(); }
	
	void GameStateManager::initialise(OgreRenderWindowPtr win, OgreCameraPtr cam, OgreSceneManagerPtr mgr )
	{
		mWindow = win;
		registerState( new DevelopperState() );
	}
	bool GameStateManager::renderOneFrame( float dt ){ return true; }
	void GameStateManager::shutdown()
	{
	}

	GameStateManager::task_iter_t GameStateManager::taskBegin(){ return mTasks.begin(); }
	GameStateManager::task_iter_t GameStateManager::taskEnd(){ return mTasks.end(); }
	GameStateManager::task_const_iter_t GameStateManager::taskBegin() const{ return mTasks.begin(); }
	GameStateManager::task_const_iter_t GameStateManager::taskEnd() const{ return mTasks.end(); }
		
	GameStateManager::task_iter_t GameStateManager::findTask( const String &name ){
		for( task_iter_t i = mTasks.begin(); i!=mTasks.end(); i++ )
			return i;
		return mTasks.end(); 
	}
	GameStateManager::task_const_iter_t GameStateManager::findTask( const String &name ) const{
		for( task_const_iter_t i = mTasks.cbegin(); i!=mTasks.cend(); i++ )
			return i;
		return mTasks.cend(); 
	}

	GameStateManager::iter_t GameStateManager::begin(){ return mStates.begin(); }
	GameStateManager::iter_t GameStateManager::end(){ return mStates.end(); }
	GameStateManager::const_iter_t GameStateManager::begin() const{ return mStates.begin(); }
	GameStateManager::const_iter_t GameStateManager::end() const{ return mStates.end(); }

	GameStateManager::iter_t GameStateManager::find( const String &name ){
		for( iter_t i = mStates.begin(); i!=mStates.end(); i++ )
			return i;
		return mStates.end(); 
	}
	GameStateManager::const_iter_t GameStateManager::find( const String &name ) const{
		for( const_iter_t i = mStates.cbegin(); i!=mStates.cend(); i++ )
			return i;
		return mStates.cend(); 
	}
	bool GameStateManager::hasState( const String &name ) const{ return find(name) != mStates.end(); }
	void GameStateManager::registerState( GameState * state, bool push_on_stack ){
		if( !state ) throw NullPointer("cannot register state", EXC_LOC);
		if( hasState( state->getName() ) ) throw ItemDuplicate("State|"+state->getName(), "GameStateManager", EXC_LOC);
		mStates.push_back( state );
		mStates.back()->create();
		if( push_on_stack ) pushState( state->getName() );
	}
	void GameStateManager::destroyState( const String &name ){
		if( !hasState( name ) ) throw ItemMissing("State|"+name, "GameStateManager", EXC_LOC);
		iter_t i = find(name);
		(*i)->destroy();
		i->destroy();
		mStates.erase( i );
	}
	bool GameStateManager::hasTask( const String &name ) const{ return findTask(name) != mTasks.end(); }
	void GameStateManager::registerTask( GameTask * task ){
		if( hasTask( task->getName() ) ) throw ItemDuplicate("Task|"+task->getName(), "GameStateManager", EXC_LOC);
		mTasks.push_back( task );
	}
	void GameStateManager::destroyTask( const String &name ){
		if( !hasTask( name ) ) throw ItemMissing("Task|"+name, "GameStateManager", EXC_LOC);
		iter_t i = find(name);
		i->destroy();
		mStates.erase( i );
	}
		
	void GameStateManager::clearStack( )
	{
		while( !mStack.empty() ) popState( );
	}
	void GameStateManager::clearStates( )
	{
		if( !mStack.empty() ) clearStack( );
		for( iter_t i=mStates.begin(); i!=mStates.end(); i++ )
		{
			(*i)->destroy();
			i->destroy();
		}
		mStates.clear();
	}
	void GameStateManager::clearTasks( )
	{
		if( !mStack.empty() ) clearStack( );
		for( task_iter_t i=mTasks.begin(); i!=mTasks.end(); i++ )
		{
			i->destroy();
		}
		mTasks.clear();
	}

	void GameStateManager::clear( )
	{
		try{
			clearStack( );
			clearStates( );
			clearTasks( );
		}
		catch( std::exception & e )
		{
			ShowMessageBox( *mWindow, e.what() );
		}
	}

	void GameStateManager::pushState( const String &name ){
		if( !hasState( name ) ) 
			throw RuntimeError(EXC_ITEM_MISSING, "Cannot push state, state \'"+name+"\' wasn\'t found", EXC_LOC);
		mStack.push(*find(name));
		mStack.top()->pushed();
	}

	void GameStateManager::popState( ){
		if( mStack.empty() ) throw RuntimeError(EXC_ITEM_MISSING, "Cannot pop state, no state on stack !", EXC_LOC);
		mStack.top()->popped();
		mStack.pop();
		if( !mStack.empty() )
			mStack.top()->pushed();
	}
	bool GameStateManager::updateStates( float dt )
	{
		if( mStates.empty() )
			throw RuntimeError(EXC_ITEM_MISSING, "Cannot update states, no state registered !", EXC_LOC);
		if( mStack.empty() )
		{
#ifndef NDEBUG
			throw RuntimeError(EXC_ITEM_MISSING, "Cannot update states, no state pushed !", EXC_LOC);
#endif
			return false;
		}

		GameStatePtr g = currentState( );
		g->update( dt );
		return true;
	}
	GameStatePtr GameStateManager::currentState() const{ return mStack.top(); }
};